home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / DTP / DTP_TEX / H220.ZIP / WP2X110.ZIP / WP2X.C < prev    next >
C/C++ Source or Header  |  1991-08-18  |  53KB  |  1,516 lines

  1. /* $Id: wp2x.c 1.10 91/08/18 15:05:41 raymond Exp $ */
  2.  
  3. /* Before compiling, read the section titled `portability concerns'. */
  4.  
  5. /************************************************************************
  6.  * $Log:    wp2x.c $
  7.  * Revision 1.10  91/08/18  15:05:41  raymond
  8.  * Descriptor file stuff.
  9.  * 
  10.  * Revision 1.9  91/08/06  09:08:09  raymond
  11.  * add missing `break' in check_arity
  12.  * 
  13.  * Revision 1.8  91/08/06  08:31:21  raymond
  14.  * Avoid infinite loop if file is corrupted.
  15.  * Better error-checking on configuration file (new output scheme).
  16.  * 
  17.  * Revision 1.7  91/08/02  13:35:37  raymond
  18.  * Epsilonically better handling of environments that didn't end properly.
  19.  * Change return type of main() to keep gcc quiet.
  20.  * MSC support.
  21.  * 
  22.  * Revision 1.6  91/07/28  21:08:53  raymond
  23.  * BeginTabs et al, FNote#, ENote#, NegateTotal, more unsupported codes
  24.  * Improve character tokens, Header, Footer
  25.  * Take care when people don't end lines with HRt
  26.  * Fix major bugs in endnote processing, footnote numbering (and nobody
  27.  *    noticed!)
  28.  * More worries about signed characters.
  29.  * 
  30.  * Revision 1.5  91/07/23  22:59:43  raymond
  31.  * Add COMMENT token, and some bug fixes.
  32.  * 
  33.  * Revision 1.4  91/07/23  22:09:23  raymond
  34.  * Concessions to slightly non-ANSI compilers. (`const', `unsigned char')
  35.  * More patches for machines with signed characters.
  36.  * Fix blatant bug in hex constants.  (Amazed nobody noticed.)
  37.  * New tags SetFn#, Header, Footer.
  38.  * Warning messages for unsupported tokens.
  39.  * Backslahes processed in character tags.
  40.  * Fixed(?) footnotes, endnotes, page length changes.
  41.  * Inserted missing `break's into the huge switch.
  42.  * 
  43.  * Revision 1.3  91/07/12  15:39:44  raymond
  44.  * Spiffy Turbo C support.
  45.  * Some <stdlib.h>'s don't declare errno et al.
  46.  * Command line switches `-s' and `-n' added.
  47.  * More cute warning messages.
  48.  * Dots periodically emitted.
  49.  * Give the enum of token types a name, to placate QuickC.
  50.  * Fix problems with pitch changes and signed characters.
  51.  * 
  52.  * Revision 1.2  91/06/22  08:18:22  raymond
  53.  * <process.h> and fputchar() aren't sufficiently portable.
  54.  * strerror() fails to exist on some so-called ANSI platforms.
  55.  * Removed assumption that characters are unsigned.
  56.  * Forgot to #include <stdarg.h>
  57.  * 
  58.  */
  59.  
  60. /************************************************************************
  61.  * PORTABILITY CONCERNS
  62.  ************************************************************************
  63.  *
  64.  * If possible, compile with unsigned characters.  (Though I think
  65.  * I've taken care of all the places where I assumed characters are
  66.  * unsigned.)
  67.  *
  68.  * This program assumes that your compiler is fully ANSI-conformant.
  69.  * Depending on how non-conformant your compiler is, you may need to
  70.  * set the following symbols at compile time:
  71.  *
  72.  * NO_CONST -- set this if your compiler does not know what `const' means.
  73.  * Cdecl    -- how to tag functions that are variadic.
  74.  *
  75.  * Cdecl is used if you need special declarations for variadic functions.
  76.  * This is used by IBM PC compilers so that you can make the default
  77.  * parameter passing Pascal-style or Fastcalls.
  78.  *
  79.  * Some very machine-dependent stuff happens when trying to open the
  80.  * descriptor file.  Please read dopen.c as well.
  81.  */
  82.  
  83. #ifdef NO_CONST
  84. #define const
  85. #endif
  86.  
  87. #ifndef Cdecl                       /* default is nothing */
  88. #define Cdecl
  89. #endif
  90.  
  91. /************************************************************************
  92.  * This program divides naturally into two parts.
  93.  *
  94.  * The first part reads in the descriptor file and builds the expansions
  95.  * for each of the identifiers listed above.
  96.  * This is the easy part.
  97.  *
  98.  * The second part reads the input file and uses the expansions collected
  99.  * in the first part to transform the file into the output.
  100.  * This is the hard part.
  101.  *
  102.  ************************************************************************/
  103.  
  104. /* And now, the code.
  105.  * We start off with some obvious header files.
  106.  */
  107.  
  108. #include <stdio.h>
  109. #include <stdarg.h>
  110. #include <stdlib.h>
  111. #include <string.h>
  112. #include <ctype.h>
  113.  
  114. /* Some platforms do not define these externals in stdlib.h */
  115. extern int   Cdecl errno;
  116. extern char *Cdecl sys_errlist[];
  117. extern int   Cdecl sys_nerr;
  118.  
  119. /************************************************************************/
  120. /* Some common idioms                                                   */
  121. /************************************************************************/
  122.  
  123. #define do_nothing /* twiddle thumbs */
  124.  
  125. /************************************************************************/
  126. /* Blowing up                                                           */
  127. /************************************************************************/
  128.  
  129. /* The function "error" accepts two arguments.  A FILE pointer and
  130.  * a printf-style argument list.  The printf-style arguments are
  131.  * printed to stderr.  If the FILE is non-NULL, the the remaining
  132.  * contents of the file are printed as well (to provide context), up
  133.  * to 80 characters.
  134.  */
  135.  
  136. void Cdecl error(FILE *fp, char *fmt, ...)
  137. {
  138.   int i;
  139.   va_list ap;
  140.  
  141.   fputs("Error: ", stderr);
  142.   va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
  143.   fputc('\n', stderr);
  144.  
  145.   if (fp) {
  146.     fprintf(stderr, "Unread text: ");
  147.     for (i = 0; i < 80 && !feof(fp); i++) fputc(getc(fp), stderr);
  148.     fputc('\n', stderr);
  149.   }
  150.   exit(1);
  151. }
  152.  
  153. /************************************************************************/
  154. /* Command-line switches                                                */
  155. /************************************************************************/
  156. int silent = 0;
  157. int blipinterval = 1024;                /* display blips every 1K */
  158. int blipcount;
  159.  
  160. /************************************************************************/
  161. /* Basic file manipulations                                             */
  162. /************************************************************************/
  163.  
  164. /* We here define a few basic functions.  Let us hope that the first
  165.  * three functions' names are self-descriptive.
  166.  */
  167.  
  168. int next_non_whitespace(FILE *fp)
  169. {
  170.   register int c;
  171.  
  172.   while ((c = getc(fp)) != EOF && isspace(c)) do_nothing;
  173.  
  174.   return c;
  175. }
  176.  
  177. int next_non_space_or_tab(FILE *fp)
  178. {
  179.   register int c;
  180.  
  181.   while ((c = getc(fp)) != EOF && (c == ' ' || c == '\t')) do_nothing;
  182.  
  183.   return c;
  184. }
  185.  
  186. void eat_until_newline(FILE *fp)
  187. {
  188.   register int c;
  189.  
  190.   while ((c = getc(fp)) != EOF && c != '\n') do_nothing;
  191. }
  192.  
  193. /* The function parse_hex grabs a (no-more-than-two-character) hex
  194.  * constant.  Similarly, parse_octal does the same for octal constants.
  195.  */
  196.  
  197. int parse_hex(FILE *fp)
  198. {
  199.   register int c, value;
  200.  
  201.   if (!isxdigit(c = toupper(getc(fp))))
  202.     error(fp, "Expecting a hex digit");
  203.  
  204.   if ((value = c - '0') > 9) value += '0' - 'A' + 10;
  205.  
  206.   if (!isxdigit(c = getc(fp))) { ungetc(c, fp); return value; }
  207.  
  208.   c = toupper(c);
  209.   value = (value << 4) + c - '0';
  210.   if (c > '9') value += '0' - 'A' + 10;
  211.   return value;
  212. }
  213.  
  214. int parse_octal(FILE *fp, register int c)
  215. {
  216.   register int value = c - '0';
  217.  
  218.   if ( (c = getc(fp)) < '0' || c > '7') { ungetc(c, fp); return value; }
  219.  
  220.   value = (value << 3) + c - '0';
  221.  
  222.   if ( (c = getc(fp)) < '0' || c > '7') { ungetc(c, fp); return value; }
  223.  
  224.   return (value << 3) + c - '0';
  225. }
  226.  
  227.  
  228. /************************************************************************/
  229. /* Storing the input strings                                            */
  230. /************************************************************************/
  231.  
  232. /* The input strings are allocated from a large pool we set up at
  233.  * startup.  This lets us do our thing without having to fight
  234.  * with people like malloc and friends.  This method does limit
  235.  * our configuration file to 32K, however.  We hope that this is
  236.  * not a problem.  (It also means that the program can be translated
  237.  * to almost any other language without too much difficulty.)
  238.  *
  239.  * Here's how it works.
  240.  *
  241.  * "pool" is an array of POOL_SIZE characters.  The value of POOL_SIZE
  242.  * is flexible, but shouldn't exceed 65535, since that's the size of
  243.  * an IBM PC segment.  If your configuration file is more than 64K,
  244.  * then there's probably something wrong.
  245.  *
  246.  * "pool_ptr" points to the next character in "pool" that hasn't been
  247.  * used for anything yet.
  248.  *
  249.  * "top_of_pool" points one character beyond the end of pool, so we can
  250.  * see if we've run out of memory.
  251.  *
  252.  * When we want to put something into the pool, we simply store into "pool"
  253.  * and increment "pool_ptr" appropriately.
  254.  *
  255.  * Access to these variables is done through the following functions,
  256.  * implemented as macros.
  257.  *
  258.  * "anchor_string()" is called before you start throwing things into
  259.  * the pool.  It returns a pointer to the beginning of the string
  260.  * being built up.
  261.  *
  262.  * "add_to_string(c)" adds the character "c" to the string being built up.
  263.  *
  264.  * "finish_string()" gets ready for building a new string.  We check
  265.  * that we did not overflow our pool.  We pull the sneaky trick of
  266.  * a dummy else clause so that [1] "else"s match up properly if this
  267.  * is nested inside an "if" statement, [2] the semicolon gets eaten
  268.  * up correctly.
  269.  *
  270.  * "remove_string(s)" removes all strings from the one called "s" onwards.
  271.  *
  272.  */
  273.  
  274. #define POOL_SIZE   32768U
  275.  
  276. char pool[POOL_SIZE];
  277. char *pool_ptr = pool;
  278. #define top_of_pool (pool + POOL_SIZE)
  279.  
  280. #define anchor_string() pool_ptr
  281. #define add_to_string(c) (*pool_ptr++ = c)
  282. #define finish_string() \
  283.      if (pool_ptr >= top_of_pool) error(NULL, "string pool overflow."); \
  284.      else do_nothing
  285. #define remove_string(s) (pool_ptr = s)
  286.  
  287. /************************************************************************/
  288. /* Remembering the expansions                                           */
  289. /************************************************************************/
  290.  
  291. /* The array "expansion" contains the expansions for everything.
  292.  * Everything is initialized to NULL.
  293.  *
  294.  * We set up things as follows:
  295.  *  expansion[0..255]  contain the expansions for the possible characters.
  296.  *  expansion[256...]  contain the expansions for the special codes.
  297.  *
  298.  * Make sure this table is kept in parallel with the names[] array
  299.  *
  300.  *
  301.  */
  302.  
  303. /*      name   value          When is it expanded? */
  304. /*      ----    ---           -------------------- */
  305. enum token_type {          /* Some compilers do not like unnamed enums */
  306.         typeout = 256,     /* Typed out as soon as it is encountered */
  307.         BEGIN        ,     /* Before the first character of the file */
  308.         END          ,     /* After the last character of the file   */
  309.         Comment      ,     /* For wp2x-generated comments            */
  310.         eComment     ,
  311.  
  312.         PageNo       ,     /* Current page number */
  313.         RomanPage    ,     /* Set page number (to roman numerals) */
  314.         ArabicPage   ,     /* Set page number (to arabic) */
  315.  
  316.         HSpace       ,     /* unbreakable space (`Hard space') */
  317.  
  318.         Tab          ,     /* Tab character */
  319.         BeginTabs    ,     /* Begin tab settings */
  320.  
  321.         /* DO NOT CHANGE THE RELATIVE ORDER OF THESE FOUR TOKENS */
  322.         SetTab       ,     /* Set normal tabstop at %d */
  323.         SetTabCenter ,     /* Set center tabstop at %d */
  324.         SetTabRight  ,     /* Set right-justified tab at %d */
  325.         SetTabDecimal,     /* Set decimal tab at %d */
  326.  
  327.         EndTabs      ,     /* End tab settings */
  328.  
  329.         HPg          ,     /* Hard page break */
  330.         CondEOP      ,     /* Conditional end-of-page */
  331.         HRt          ,     /* Hard return */
  332.         SRt          ,     /* Soft return */
  333.  
  334.         NHyph        ,     /* Normal hyphen */
  335.         NHyphE       ,     /* Normal hyphen at the end of a line */
  336.         HHyph        ,     /* Hard (nonbreakable) hyphen */
  337.         DHyph        ,     /* Discretionary hyphen */
  338.         DHyphE       ,     /* Discretionary hyphen at the end of a line */
  339.         NoHyphWord   ,     /* Do not hyphenate this word */
  340.  
  341.         Marg         ,     /* Margin settings */
  342.         TopMarg      ,     /* Set top margin */
  343.         PageLength   ,     /* Set page length */
  344.  
  345.         SS           ,     /* Single spacing */
  346.         DS           ,     /* Double spacing */
  347.         OHS          ,     /* 1.5 spacing (One and a Half Spacing) */
  348.         TS           ,     /* Triple spacing */
  349.         LS           ,     /* Generic line spacing */
  350.         LPI          ,     /* set 6 or 8 LPI */
  351.  
  352.         Bold         ,     /* Begin boldface */
  353.         eBold        ,     /* End boldface */
  354.         Und          ,     /* Begin underline */
  355.         eUnd         ,     /* End underline */
  356.         Red          ,     /* Begin redline */
  357.         eRed         ,     /* End redline */
  358.         Strike       ,     /* Begin strikeout */
  359.         eStrike      ,     /* End strikeout */
  360.         Rev          ,     /* Begin reverse video */
  361.         eRev         ,     /* End reverse video */
  362.  
  363.         Over         ,     /* Overstrike */
  364.         eOver        ,     /* [mythical "end overstroke" code] */
  365.         Sup          ,     /* Superscript */
  366.         eSup         ,     /* [mythical "end superscript" code] */
  367.         Sub          ,     /* Subscript */
  368.         eSub         ,     /* [mythical "end subscript" code] */
  369.  
  370.         UpHalfLine   ,     /* Advance printer up 1/2 line */
  371.         DownHalfLine ,     /* Advance printer down 1/2 line */
  372.         AdvanceToHalfLine, /* Advance to absolute vertical position */
  373.  
  374.         Indent       ,     /* Indented paragraph */
  375.         DIndent      ,     /* Left-and-right-indented paragraph */
  376.         eIndent      ,     /* End indented paragraph */
  377.         MargRel      ,     /* Margin release (unknown argument) */
  378.  
  379.         Center       ,     /* Center current line */
  380.         eCenter      ,     /* End centering */
  381.         CenterHere   ,     /* Center line around current column */
  382.         eCenterHere  ,     /* End centering */
  383.  
  384.         Align        ,     /* Begin alignment */
  385.         eAlign       ,     /* End alignment */
  386.         AlignChar    ,     /* Set alignment character */
  387.         FlushRight   ,     /* Begin flush right */
  388.         eFlushRight  ,     /* End flush right */
  389.  
  390.         Math         ,     /* Begin math mode */
  391.         eMath        ,     /* End math mode */
  392.         MathCalc     ,     /* Begin math calc mode */
  393.         MathCalcColumn,    /* Math calc column */
  394.  
  395.         SubTtl       ,     /* Do subtotal */
  396.         IsSubTtl     ,     /* Subtotal entry */
  397.         Ttl          ,     /* Do total */
  398.         IsTtl        ,     /* Total entry */
  399.         GrandTtl     ,     /* Do grand total */
  400.         NegateTotal  ,     /* Negate current total */
  401.  
  402.         Col          ,     /* Begin column mode */
  403.         eCol         ,     /* End column mode */
  404.  
  405.         Fn           ,     /* Begin footnote */
  406.         eFn          ,     /* End footnote */
  407.         En           ,     /* Begin endnote */
  408.         eEn          ,     /* End endnote */
  409.         SetFnNum     ,     /* Set footnote number */
  410.         FNoteNum     ,     /* Footnote number */
  411.         ENoteNum     ,     /* Endnote number */
  412.         TableMarker  ,     /* Table of contents or whatever marker */
  413.  
  414.         Hyph         ,     /* Hyphenation on */
  415.         eHyph        ,     /*             off */
  416.         Just         ,     /* Justification on */
  417.         eJust        ,     /*               off */
  418.         Wid          ,     /* Widow/orphan protection on */
  419.         eWid         ,     /*                         off */
  420.         HZone        ,     /* Hyphenation zone */
  421.         DAlign       ,     /* Decimal alignment character */
  422.  
  423.         Header       ,     /* Begin header text */
  424.         eHeader      ,     /* End header text */
  425.         Footer       ,     /* Begin footer text */
  426.         eFooter      ,     /* End footer text */
  427.  
  428.         Supp         ,     /* Suppress formatting for one page */
  429.         CtrPg        ,     /* Center page vertically */
  430.  
  431.         SetFont      ,     /* Change pitch or font */
  432.         SetBin       ,     /* Select paper bin (0, 1, ...) */
  433.  
  434.         PN           ,     /* Page number position (PN+0 through PN+8) */
  435.  
  436. /* Internal tokens for unsupported operations */
  437.         UnsupportedPlaceHolder = PN + 9,
  438.         SetPageNumberColumn,
  439.         SetTabs,
  440.         SetUnderlineMode,
  441.         DefineColumn,
  442.         SetFootnoteAttributes,
  443.         SetParagraphNumberingStyle,
  444.         NumberedParagraph,
  445.         BeginMarkedText,
  446.         EndMarkedText,
  447.         DefineMarkedText,
  448.         DefineIndexMark,
  449.         DefineMathColumns,
  450.         Obsolete,
  451.         ReservedCode,
  452.         UnknownCode,
  453.         LastToken
  454. };
  455.  
  456. char *expansion[LastToken];
  457.  
  458.  
  459. /************************************************************************/
  460. /* Naming the identifiers                                               */
  461. /************************************************************************/
  462. /* Extreme care must be taken to ensure that this list parallels the list
  463.  * of token names above.
  464.  */
  465.  
  466. typedef struct identifier {
  467.     char *name;
  468.     int arity;
  469. } Identifier;
  470.  
  471. Identifier names[] = {
  472.     { "typeout", 0 },
  473.     { "BEGIN", 0 },
  474.     { "END", 0 },
  475.     { "Comment", 0 },
  476.     { "comment", 0 },
  477.     { "PageNo", 0 },
  478.     { "RomanPage", 1 },
  479.     { "ArabicPage", 1 },
  480.     { "HSpace", 0 },
  481.     { "Tab", 0 },
  482.     { "BeginTabs", 0 },
  483.     { "SetTab", 1 },
  484.     { "SetTabCenter", 1 },
  485.     { "SetTabRight", 1 },
  486.     { "SetTabDecimal", 1 },
  487.     { "EndTabs", 0 },
  488.     { "HPg", 0 },
  489.     { "CondEOP", 1 },
  490.     { "HRt", 0 },
  491.     { "SRt", 0 },
  492.     { "-", 0 },        /* NHyph */
  493.     { "--", 0 },       /* NHyphE */
  494.     { "=", 0 },        /* HHyph */
  495.     { "\\-", 0 },      /* DHyph */
  496.     { "\\--", 0 },     /* DHyphE */
  497.     { "NoHyphWord", 0 },
  498.     { "Marg", 2 },
  499.     { "TopMarg", 1 },
  500.     { "PageLength", 1 },
  501.     { "SS", 0 },
  502.     { "DS", 0 },
  503.     { "1.5S", 0 },    /* OHS */
  504.     { "TS", 0 },
  505.     { "LS", 1 },
  506.     { "LPI", 1 },
  507.     { "Bold", 0 },
  508.     { "bold", 0 },
  509.     { "Und", 0 },
  510.     { "und", 0 },
  511.     { "Red", 0 },
  512.     { "red", 0 },
  513.     { "Strike", 0 },
  514.     { "strike", 0 },
  515.     { "Rev", 0 },
  516.     { "rev", 0 },
  517.     { "Over", 0 },
  518.     { "over", 0 },
  519.     { "Sup", 0 },
  520.     { "sup", 0 },
  521.     { "Sub", 0 },
  522.     { "sub", 0 },
  523.     { "UpHalfLine", 0 },
  524.     { "DownHalfLine", 0 },
  525.     { "AdvanceToHalfLine", 2 },
  526.     { "Indent", 0 },
  527.     { "DIndent", 0 },
  528.     { "indent", 0 },
  529.     { "MarginRelease", 1 },
  530.     { "Center", 0 },
  531.     { "center", 0 },
  532.     { "CenterHere", 0 },
  533.     { "centerhere", 0 },
  534.     { "Align", 0 },
  535.     { "align", 0 },
  536.     { "AlignChar", 1 },
  537.     { "FlushRight", 0 },
  538.     { "flushright", 0 },
  539.     { "Math", 0 },
  540.     { "math", 0 },
  541.     { "MathCalc", 0 },
  542.     { "MathCalcColumn", 0 },
  543.     { "SubTotal", 0 },
  544.     { "IsSubTotal", 0 },
  545.     { "Total", 0 },
  546.     { "IsTotal", 0 },
  547.     { "GrandTotal", 0 },
  548.     { "NegateTotal", 0 },
  549.     { "Col", 0 },
  550.     { "col", 0 },
  551.     { "Fn", 0 },
  552.     { "fn", 0 },
  553.     { "En", 0 },
  554.     { "en", 0 },
  555.     { "SetFn#", 1 },
  556.     { "FNote#", 0 },
  557.     { "ENote#", 0 },
  558.     { "TableMarker", 0 },
  559.     { "Hyph", 0 },
  560.     { "hyph", 0 },
  561.     { "Just", 0 },
  562.     { "just", 0 },
  563.     { "Wid", 0 },
  564.     { "wid", 0 },
  565.     { "HZone", 2 },
  566.     { "DAlign", 1 },
  567.     { "Header", 0 },
  568.     { "header", 0 },
  569.     { "Footer", 0 },
  570.     { "footer", 0 },
  571.     { "Supp", 1 },
  572.     { "CtrPg", 0 },
  573.     { "SetFont", 2 },
  574.     { "SetBin", 1 },
  575.     { "PN0", 0 },
  576.     { "PN1", 0 },
  577.     { "PN2", 0 },
  578.     { "PN3", 0 },
  579.     { "PN4", 0 },
  580.     { "PN5", 0 },
  581.     { "PN6", 0 },
  582.     { "PN7", 0 },
  583.     { "PN8", 0 },
  584.     { NULL, 0 },  /* UnsupportedPlaceHolder -- keeps match_identifier happy */
  585.     { "set page number column", 0 },
  586.     { "extended tabs", 0 },
  587.     { "underline mode", 0 },
  588.     { "define column", 0 },
  589.     { "footnote attributes", 0 },
  590.     { "paragraph numbering style", 0 },
  591.     { "numbered paragraph", 0 },
  592.     { "begin marked text", 0 },
  593.     { "end marked text", 0 },
  594.     { "define marked text", 0 },
  595.     { "define index mark", 0 },
  596.     { "define math columns", 0 },
  597.     { "WPCorp obsolete", 0 },
  598.     { "WPCorp reserved", 0 },
  599.     { "WPCorp undefined", 0 },
  600. };
  601.  
  602. /* The file pointer "descriptor" points to our descriptor file
  603.  * and "input" points to our input file.
  604.  *
  605.  * Kinda makes sense that way.
  606.  */
  607.  
  608. FILE *descriptor, *input;
  609.  
  610. /* And the function match_identifier(s) takes a string and converts
  611.  * it to its corresponding integer.  Or blows up if it couldn't
  612.  * find one.
  613.  */
  614.  
  615. int match_identifier(const char *s)
  616. {
  617.   Identifier *I;
  618.  
  619.   /* Maybe it is a special character */
  620.   if (s[0] == '\'' && s[2] == '\'' && s[3] == '\0')
  621.     return (int) (unsigned char) s[1];
  622.  
  623.   /* Else it must be a multi-character guy */
  624.   for (I = names; I->name; I++)
  625.     if (!strcmp(I->name, s)) return typeout + (I - names);
  626.  
  627.   /* Otherwise, I don't know what to do with it */
  628.   error(descriptor, "Unknown identifier %s", s);
  629.   /*NOTREACHED*/
  630.   return 0;
  631. }
  632.  
  633. /* check_arity ensures that the expansion string is valid */
  634. void check_arity(int ident, char *t)
  635. {
  636.   char *s;
  637.   int arity = 0;
  638.   if (ident > typeout) arity = names[ident-typeout].arity;
  639.   for (s = t; *s; s++) {
  640.     if (*s != '%') continue;
  641.     switch (*++s) {
  642.     case '\n':
  643.       if (s != t+1)
  644.         error(descriptor, "%s: `%%\\n' not at start of expansion",
  645.               names[ident-typeout].name);
  646.         break;
  647.     case '1':
  648.     case 'c':
  649.       if (arity < 1) goto bad_escape;
  650.       break;
  651.     case '2':
  652.       if (arity < 2) goto bad_escape;
  653.       break;
  654.     case '%':
  655.       break;
  656.     default:
  657. bad_escape:
  658.       error(descriptor, "%s: invalid escape `%%%c'", names[ident-typeout].name, *s);
  659.     }
  660.   }
  661. }
  662.  
  663. /************************************************************************/
  664. /* Reading input from the descriptor file                               */
  665. /************************************************************************/
  666.  
  667. /* The macro igetc() gets a character from the input file.
  668.  * the macro dgetc() gets a character from the descriptor file.
  669. */
  670.  
  671. #define igetc() getc(input)
  672. #define dgetc() getc(descriptor)
  673.  
  674. /* expand_backslash() is called when a backslash is encountered in
  675.  * the descriptor file.  Its job is to parse a backslash-sequence.
  676.  * The usual C-escapes (\a \b \f \n \r \t \v) are understood, as
  677.  * well as the octal escape \000 [up to three octal digits] and
  678.  * the hex escape \xFF [up to two hex digits].
  679.  */
  680.  
  681. int expand_backslash(void) {
  682.     int c;
  683.  
  684.     switch (c = dgetc()) {
  685.     case 'a': c = '\a'; break;
  686.     case 'b': c = '\b'; break;
  687.     case 'f': c = '\f'; break;
  688.     case 'n': c = '\n'; break;
  689.     case 'r': c = '\r'; break;
  690.     case 't': c = '\t'; break;
  691.     case 'v': c = '\v'; break;
  692.     case 'x':
  693.     case 'X': c = parse_hex(descriptor); break;
  694.     case '0':
  695.     case '1':
  696.     case '2':
  697.     case '3':
  698.     case '4':
  699.     case '5':
  700.     case '6':
  701.     case '7': c = parse_octal(descriptor, c); break;
  702.     default:  /* c = c; */ break;
  703.     }
  704.     return c;
  705. }
  706.  
  707. /* The function read_identifier() attempts to match an identifier
  708.  * in the descriptor file.  It returns EOF if the end of the descriptor
  709.  * file was reached, or the code of the identifier we found.
  710.  * (or blows up if an error was detected.)
  711.  * We build the identifier in "s", with the help of our
  712.  * pool-managing functions above, then discard it, immediately,
  713.  * since we don't use it any more.
  714.  */
  715.  
  716. int read_identifier(void)
  717. {
  718.   register int c;      /* A character we have read */
  719.   char *s;    /* The identifier we are building */
  720.   int ident;   /* The identifier we found */
  721.  
  722.   /* Skip over comments */
  723.   while ((c = next_non_whitespace(descriptor)) == '#')
  724.       eat_until_newline(descriptor);
  725.  
  726.   if (c == EOF) return EOF;
  727.  
  728.   /* At this point, "c" contains the first letter of a potential
  729.    * identifier.  Let's see what it could possibly be.
  730.    */
  731.   s = anchor_string();
  732.   if (c == '\'') {                      /* a character token */
  733.     add_to_string(c);
  734.     if ((c = dgetc()) == '\\') c = expand_backslash();
  735.     add_to_string(c);
  736.     if ((c = dgetc()) != '\'')
  737.       error(descriptor, "Invalid character identifier");
  738.     add_to_string(c);
  739.     c = next_non_space_or_tab(descriptor);
  740.   } else do {                           /* a name token */
  741.     add_to_string(c);
  742.     c = next_non_space_or_tab(descriptor);
  743.     if (c == '\\') c = expand_backslash();
  744.   } while (c != EOF && c != '=' && c != '\n');
  745.  
  746.   if (c != '=') error(descriptor, "Identifier not followed by = sign");
  747.       /* A boo-boo.  Something bad happened. */
  748.  
  749.   add_to_string('\0');   /* Make it a standard C string. */
  750.   finish_string();
  751.  
  752.   ident = match_identifier(s); /* Go find one. */
  753.  
  754.   remove_string(s); /* And we're done with it now. */
  755.  
  756.   return ident;
  757. }
  758.  
  759. /* The function grab_expansion() reads expansion text from the
  760.  * descriptor file and adds it to the pool, returning a pointer
  761.  * to the string it just created.
  762.  *
  763.  * After anchoring a new string, we look for the opening quotation
  764.  * mark, then start gobbling characters.  Everything gets copied
  765.  * straight into the string.
  766.  *
  767.  */
  768.  
  769. char *grab_expansion(void)
  770. {
  771.   register int c; /* Characters being read */
  772.   char *s;   /* The string we are building */
  773.  
  774.   s = anchor_string();
  775.  
  776.   if (next_non_whitespace(descriptor) != '\"')
  777.     error(descriptor, "Quotation mark expected");
  778.  
  779.   /* Now read the stream until we hit another quotation mark. */
  780.  
  781.   while ((c = dgetc()) != EOF && c != '\"') {
  782.     if (c == '\\') c = expand_backslash();
  783.     add_to_string(c);
  784.   }
  785.   add_to_string('\0');
  786.   finish_string();
  787.   return s;
  788. }
  789.  
  790. /* Ah, now with all of these beautiful functions waiting for us,
  791.  * we can now write our first Useful Function:  do_descriptor_file.
  792.  * It reads the descriptor file and loads up the "expansion" array
  793.  * with the text expansions we are reading from the file.
  794.  *
  795.  * If we grabbed the expansion of a "typeout", we type it out
  796.  * and discard the string.
  797.  *
  798.  * We stop when the descriptor file runs dry.
  799.  *
  800.  */
  801.  
  802. void do_descriptor_file(void)
  803. {
  804.   register int ident;
  805.  
  806.   while ((ident = read_identifier()) != EOF) {
  807.     expansion[ident] = grab_expansion();
  808.     if (ident == typeout && !silent) {
  809.       fputs(expansion[typeout], stderr); remove_string(expansion[typeout]);
  810.       expansion[typeout] = NULL;
  811.     } else check_arity(ident, expansion[ident]);
  812.   }
  813. }
  814.  
  815. /************************************************************************/
  816. /* Reading from the input file                                          */
  817. /************************************************************************/
  818.  
  819. /* The function verify(c) checks that the next character in the input
  820.  * stream is indeed "c".  It eats the character, if all is well.
  821.  * If something went wrong, we complain to stderr, but keep going.
  822.  */
  823.  
  824. void verify(int c)
  825. {
  826.   int d = igetc();
  827.   if (d != c) fprintf(stderr, "Warning: Expected %02X but received %02X.\n", c, d);
  828. }
  829.  
  830. /* The function gobble(n) simply eats "n" characters from the input
  831.  * file.
  832.  */
  833. void gobble(int n)
  834. {
  835.   while (n--) (void) igetc();
  836. }
  837.  
  838. int last_HRt = 0;                       /* most recent output was HRt */
  839.  
  840. /* Processing a special code simply entails dumping its expansion.
  841.  * If the expansion is NULL, then we either
  842.  *   [1] print nothing, if it is a code,
  843.  *   [2] print the character itself, if it is an ASCII character.
  844.  *
  845.  * In dumping its expansion, we expand the following percent-escapes:
  846.  *
  847.  *  The percent-escapes are:
  848.  *      %\n  -- newline if previous character was not a newline
  849.  *              (meaningful only as first character in sequence)
  850.  *      %1   -- first parameter, in decimal form
  851.  *      %2   -- second parameter, in decimal form
  852.  *      %c   -- first parameter, in character form
  853.  *      %%   -- literal percent sign
  854.  *
  855.  *  all other %-escapes are flagged as warnings (but should never occur,
  856.  *  since they are trapped at the time the descriptor file is read.)
  857.  */
  858. void process(int c, int d1, int d2)
  859. {
  860.   char *s;
  861.   static int last_newline = 0;
  862.  
  863.   last_HRt = 0;                         /* the killer switch sets this */
  864.  
  865.   if (expansion[c] == NULL) {           /* invent a default action */
  866.     if (c >= ' ' && c < 128) {
  867.       putchar(c);                       /* regular characters emit themselves */
  868.       last_newline = 0;
  869.       return;
  870.     } else if (c < 256) {               /* single character */
  871.       expansion[c] = anchor_string(); /* emits itself */
  872.       add_to_string(c); add_to_string('\0');
  873.       finish_string();
  874.       if (!silent) fprintf(stderr, "Warning: No expansion for %02X (%c)\n", c, c);
  875.     } else {                            /* provide null expansion */
  876.       expansion[c] = "";
  877.       if (!silent) {
  878.         fprintf(stderr, "Warning: No expansion for %s\n", names[c-typeout].name);
  879.       }
  880.     }
  881.   }
  882.  
  883.   s = expansion[c];
  884.   if (!*s) return;    /* the rest of the code assumes non-null string */
  885.   do {
  886.     if (*s != '%') putchar(*s++);
  887.     else {
  888.       s++;
  889.       switch (*s++) {
  890.       case '\n':
  891.         if (!last_newline) putchar('\n'); break;
  892.       case '1':
  893.         printf("%d", d1); break;
  894.       case '2':
  895.         printf("%d", d2); break;
  896.       case 'c':
  897.         putchar(d1); break;
  898.       case '%':
  899.         putchar('%'); break;
  900.       default:
  901.         fprintf(stderr, "Internal error:  Invalid escape, %%%c\n", s[-1]);
  902.         break;
  903.       }
  904.     }
  905.   } while (*s);
  906.   last_newline = s[-1] == '\n';
  907. }
  908.  
  909. #define process0(c)     process(c,0,0)
  910. #define process1(c,a)   process(c,a,0)
  911. #define process2(c,a,b) process(c,a,b)
  912.  
  913. void unsupported(int c)
  914. {
  915.   if (!silent && !expansion[c]) {
  916.     expansion[c] = "";
  917.     fprintf(stderr, "Warning: `%s' code not supported\n", names[c-typeout].name);
  918.   }
  919.   process0(Comment); fputs(names[c-typeout].name, stdout); process0(eComment);
  920. }
  921.  
  922. /* The function gobble_until(c) eats characters from the input file
  923.  * until it reaches a c or reaches EOF.
  924.  */
  925. void gobble_until(int c)
  926. {
  927.   int i;
  928.   while ((i = igetc()) != EOF && (int) (unsigned char) i != c) do_nothing;
  929. }
  930.  
  931. /* line_spacing(l) is called whenever we hit a line-spacing-change command.
  932.  * The argument is the desired line spacing, multiplied by two.
  933.  * So single spacing gets a 2, 1.5 spacing gets a 3, etc.
  934.  */
  935. void line_spacing(int l)
  936. {
  937.   switch (l) {
  938.     case 2: process0(SS); break;
  939.     case 3: process0(OHS); break;
  940.     case 4: process0(DS); break;
  941.     case 6: process0(TS); break;
  942.     default: process1(LS, l); break;
  943.   }
  944. }
  945.  
  946. int environment_status = 0;             /* cleanup at HRt */
  947. void leave_environment(int force_HRt) {
  948.     if (environment_status) {
  949.       process0(environment_status);
  950.       environment_status = 0;
  951.     }
  952.     if (force_HRt && !last_HRt) process0(HRt);
  953. }
  954.  
  955. /* The "note_status" flag has one of three values:
  956.  *    0   if we are not inside a note
  957.  *    1   if we are inside a footnote
  958.  *    2   if we are inside an endnote
  959.  *
  960.  * The function handle_note() is called to deal with footnotes and
  961.  * endnotes.  It adjusts the note_status accordingly.
  962.  */
  963.  
  964. int note_status = 0;
  965.  
  966. void handle_note(void)
  967. {
  968.   if (note_status) {
  969.     leave_environment(1); process0(note_status); note_status = 0;
  970.   } else {          /* Decide whether it is an endnote or a footnote */
  971.     if (igetc() & 2)  { process0(En); note_status = eEn; gobble(5); }
  972.                 else  { process0(Fn); note_status = eFn; gobble(7); }
  973.     verify(0xFF);
  974.     gobble(2);                                  /* margins */
  975.   }
  976. }
  977.  
  978. /* The tab_table is a bit field.  Each set bit represents a tabstop.
  979.  * Note, however, that the bits are counted from MSB to LSB.
  980.  *
  981.  * The tab_attribute_table is a nybble field.  The n'th nybble represents
  982.  * the attributes of the n'th tabstop.
  983.  */
  984. unsigned char tab_table[32];
  985. unsigned char tab_attribute_table[20];
  986. int next_attribute;
  987.  
  988. void process_tab_attribute(int i) {
  989.     int b;
  990.  
  991.     if (next_attribute & 1) b = tab_attribute_table[next_attribute/2] & 3;
  992.     else b = (tab_attribute_table[next_attribute/2] / 16) & 3;
  993.     next_attribute++;
  994.  
  995.     /* Bottom two bites define what kind of tab.
  996.      * Bit 2 is set if we need dot filling.
  997.      * Bit 3 is unused.
  998.      * We `&3' above because we won't support dot filling.
  999.      */
  1000.     process1(SetTab + b, i);
  1001. }
  1002.  
  1003. void process_tab_table(void) {
  1004.     int i;
  1005.     next_attribute = 0;
  1006.  
  1007.     process0(BeginTabs);
  1008.     for (i = 0; i < 32; i++) {
  1009.         if (tab_table[i] == 0) continue;    /* early out */
  1010.         if (tab_table[i] & 0x80) process_tab_attribute(i * 8 + 0);
  1011.         if (tab_table[i] & 0x40) process_tab_attribute(i * 8 + 1);
  1012.         if (tab_table[i] & 0x20) process_tab_attribute(i * 8 + 2);
  1013.         if (tab_table[i] & 0x10) process_tab_attribute(i * 8 + 3);
  1014.         if (tab_table[i] & 0x08) process_tab_attribute(i * 8 + 4);
  1015.         if (tab_table[i] & 0x04) process_tab_attribute(i * 8 + 5);
  1016.         if (tab_table[i] & 0x02) process_tab_attribute(i * 8 + 6);
  1017.         if (tab_table[i] & 0x01) process_tab_attribute(i * 8 + 7);
  1018.     }
  1019.     process0(EndTabs);
  1020. }
  1021.  
  1022. void handle_tabs(void) {
  1023.     /* pad the tables to force no new tabs, and left tabs everywhere */
  1024.     memset(tab_table, 0, sizeof(tab_table));
  1025.     memset(tab_attribute_table, 0, sizeof(tab_attribute_table));
  1026.  
  1027.     fread(tab_table, 20, 1, input);     /* old-style tabs */
  1028.     process_tab_table();
  1029. }
  1030.  
  1031. void handle_extended_tabs(void) {
  1032.     fread(tab_table, 32, 1, input);
  1033.     fread(tab_attribute_table, 20, 1, input);
  1034.     process_tab_table();
  1035. }
  1036.  
  1037. /* The FF_status flag tells us what we should do when we encounter an 0xFF.
  1038.  * It contains the token code of the active code, or 0 if no code is active.
  1039.  */
  1040.  
  1041. int FF_status = 0;
  1042.  
  1043. void handle_FF(void)
  1044. {
  1045.     if (FF_status) {                            /* finish header/footer */
  1046.         leave_environment(1);
  1047.         process0(FF_status);
  1048.         gobble(2);
  1049.         verify(0xD1);
  1050.         FF_status = 0;
  1051.     } else process0(0xFF);
  1052. }
  1053.  
  1054. /* The function process_token does all of the real work.
  1055.  * Given the first character of a token, we eat up everything
  1056.  * that belongs to that token.  This routine might be called
  1057.  * recursively, since some tokens are defined in terms of other
  1058.  * tokens.  (For example, the subscript code is expanded as
  1059.  *   [Sub] <character being subscripted> [sub]
  1060.  * and the <character being subscripted> might involve other token
  1061.  * expansions; specifically, it might be an IBM Extended character.)
  1062.  *
  1063.  * Luckily, most of our tokens are not recursive.  The macro
  1064.  *     bracket(before, after)
  1065.  * does the recursive stuff for us, bracketing the next token
  1066.  * between expansions of "before" and "after".
  1067.  *
  1068.  */
  1069.  
  1070. #define bracket(before,after) process0(before); process_token(); \
  1071.                               process0(after);
  1072.  
  1073. int process_token(void)
  1074. {
  1075.   int c = igetc();
  1076.  
  1077.   if (c == EOF) return 0;
  1078.  
  1079.   c = (int) (unsigned char) c;
  1080.  
  1081.   if (!--blipcount && !silent) {
  1082.     blipcount = blipinterval;
  1083.     putc('.', stderr);
  1084.   }
  1085.  
  1086.   switch (c) {   /* Codes listed in numerical rather than logical order */
  1087.  
  1088.    case 0x02: process0(PageNo); break;                   /* Page number */
  1089.  
  1090.    case 0x09: process0(Tab); break;                    /* Tab character */
  1091.  
  1092.    case 0x8C:                            /* Soft page break after a HRt */
  1093.    case 0x0A:                                            /* Hard Return */
  1094.               last_HRt = 0; leave_environment(1); last_HRt = 1; break;
  1095.    case 0x0B:                            /* Soft page break after a SRt */
  1096.    case 0x0D: process0(SRt); break;                      /* Soft Return */
  1097.  
  1098.    case 0x0C: process0(HPg); break;                        /* Hard Page */
  1099.  
  1100.    case '-' : process0(HHyph); break;             /* Nonbreaking hyphen */
  1101.  
  1102.    case 0x80: break;                                             /* NOP */
  1103.    case 0x81: process0(Just); break;             /* Right justification */
  1104.    case 0x82: process0(eJust); break;                   /* Ragged right */
  1105.    case 0x83:                                          /* End centering */
  1106.    case 0x84: leave_environment(0); break;          /* End aligned text */
  1107.    case 0x85: process0(MathCalc); break;             /* Begin math calc */
  1108.    case 0x86: process0(CtrPg); break;         /* Center page vertically */
  1109.    case 0x87: process0(Col); break;                /* Begin column mode */
  1110.    case 0x88: process0(eCol); break;                 /* End column mode */
  1111.    case 0x89: process0(Tab); break;           /* Tab after right margin */
  1112.    case 0x8A: process0(Wid); break;          /* Widow/orphan protection */
  1113.    case 0x8B: process0(eWid); break;            /* Allow widows/orphans */
  1114. /* case 0x8C: see 0x0A */
  1115.    case 0x8D:                                /* Footnote/Endnote number */
  1116.               process0(note_status == eFn ? FNoteNum : ENoteNum); break;
  1117.    case 0x8E:
  1118.    case 0x8F: unsupported(ReservedCode); break;       /* Reserved codes */
  1119.    case 0x90: process0(Red); break;                    /* Begin redline */
  1120.    case 0x91: process0(eRed); break;                     /* End redline */
  1121.    case 0x92: process0(Strike); break;               /* Begin strikeout */
  1122.    case 0x93: process0(eStrike); break;                /* End strikeout */
  1123.    case 0x94: process0(Und); break;                /* Begin underlining */
  1124.    case 0x95: process0(eUnd); break;                 /* End underlining */
  1125.    case 0x96: process0(Rev); break;              /* Begin reverse video */
  1126.    case 0x97: process0(eRev); break;               /* End reverse video */
  1127.    case 0x98: process0(TableMarker); break;/* Table of something marker */
  1128.    case 0x99: bracket(Over, eOver); break;                /* Overstrike */
  1129.    case 0x9A: process0(NoHyphWord); break;/* Do not hyphenate this word */
  1130.    case 0x9B: break;                           /* End of generated text */
  1131.    case 0x9C: process0(eBold); break;                   /* End boldface */
  1132.    case 0x9D: process0(Bold); break;                  /* Begin boldface */
  1133.    case 0x9E: process0(eHyph); break;             /* Forbid hyphenation */
  1134.    case 0x9F: process0(Hyph); break;               /* Allow hyphenation */
  1135.    case 0xA0: process0(HSpace); break;                    /* Hard space */
  1136.    case 0xA1: process0(SubTtl); break;                   /* Do subtotal */
  1137.    case 0xA2: process0(IsSubTtl); break;              /* Subtotal entry */
  1138.    case 0xA3: process0(Ttl); break;                         /* Do total */
  1139.    case 0xA4: process0(IsTtl); break;                    /* Total entry */
  1140.    case 0xA5: process0(GrandTtl); break;              /* Do grand total */
  1141.    case 0xA6: process0(MathCalcColumn); break;      /* Math calc column */
  1142.    case 0xA7: process0(Math); break;                 /* Begin math mode */
  1143.    case 0xA8: process0(eMath); break;                  /* End math mode */
  1144.    case 0xA9: process0(NHyph); break;        /* Normal breakable hyphen */
  1145.    case 0xAA:                                  /* Hyphen at end of line */
  1146.    case 0xAB: process0(NHyphE); break;         /* Hyphen at end of page */
  1147.    case 0xAC: process0(DHyph); break;           /* Discretionary hyphen */
  1148.    case 0xAD:                           /* Discretionary hyphen at EOLn */
  1149.    case 0xAE: process0(DHyphE); break;  /* Discretionary hyphen at EOPg */
  1150.    case 0xAF:                                   /* EOT columns and EOLn */
  1151.    case 0xB0: break;                            /* EOT columns and EOPg */
  1152.  
  1153.    case 0xB1: process0(NegateTotal); break;     /* Negate current total */
  1154.  
  1155.    case 0xBC: bracket(Sup, eSup); break;                 /* Superscript */
  1156.    case 0xBD: bracket(Sub, eSub); break;                   /* Subscript */
  1157.    case 0xBE: process0(UpHalfLine); break;       /* Advance 1/2 line up */
  1158.    case 0xBF: process0(DownHalfLine); break;   /* Advance 1/2 line down */
  1159.  
  1160.    case 0xC0: gobble(2); c = igetc();                  /* Margin change */
  1161.               process2(Marg, c, igetc()); verify(0xC0); break;
  1162.  
  1163.    case 0xC1: gobble(1); line_spacing(igetc()); verify(0xC1); break;
  1164.                                                  /* Line spacing change */
  1165.  
  1166.    case 0xC2: process1(MargRel, igetc());             /* Margin release */
  1167.               verify(0xC2); break;
  1168.  
  1169.  
  1170.    case 0xC3:                                            /* Center text */
  1171.               leave_environment(0);
  1172.               switch (igetc()) {
  1173.               case 0: process0(Center);       /* Center between margins */
  1174.                       environment_status = eCenter; break;
  1175.               case 1:                   /* Center around current column */
  1176.                       process0(CenterHere);
  1177.                       environment_status = eCenterHere; break;
  1178.               }
  1179.               gobble(2); verify(0xC3); break;
  1180.  
  1181.    case 0xC4:                                   /* Align or Flush Right */
  1182.               leave_environment(0);
  1183.               c = igetc();
  1184.               /* if high bit on c is set, then dot fill.  (Ignore)      */
  1185.               switch (c & 0x7f) {
  1186.               case 0x0C:
  1187.               case 0x0A: process1(FlushRight, igetc());/* alignment col */
  1188.                          environment_status = eFlushRight;
  1189.                          break;
  1190.               default:   process2(Align, c, igetc());/* alignment column */
  1191.                          environment_status = eAlign;
  1192.                          break;
  1193.               }
  1194.               gobble(1);                                       /* trash */
  1195.               verify(0xC4);
  1196.               break;
  1197.  
  1198.    case 0xC5: gobble(2); c = igetc();               /* Hyphenation zone */
  1199.               process2(HZone, c, igetc()); verify(0xC5); break;
  1200.  
  1201.    case 0xC6: gobble(1);                        /* Page number position */
  1202.               process0(PN + igetc()); verify(0xC6); break;
  1203.  
  1204.    case 0xC7: gobble(2); c = igetc();                /* New page number */
  1205.               c = (c<<8) + (unsigned char)igetc();
  1206.               process1( (c&0x8000) ? RomanPage : ArabicPage, c&0x7fff);
  1207.               verify(0xC7); break;
  1208.  
  1209.    case 0xC8: gobble(3);                      /* Set Page number column */
  1210.               /* next 3 bytes are <left> <center> <right> */
  1211.               gobble(3);
  1212.               unsupported(SetPageNumberColumn);
  1213.               verify(0xC8); break;
  1214.  
  1215.    case 0xC9: gobble(20);                                   /* Set tabs */
  1216.               handle_tabs();
  1217.               verify(0xC9); break;
  1218.  
  1219.    case 0xCA: process1(CondEOP, igetc());    /* Conditional end of page */
  1220.               verify(0xCA); break;
  1221.  
  1222.    case 0xCB:                                      /* Set pitch or font */
  1223.               gobble(2);                          /* old pitch and font */
  1224.               c = igetc();
  1225.               process2(SetFont, c, igetc());   /* pitch and font number */
  1226.                               /* negative pitch means proportional font */
  1227.               verify(0xCB); break;
  1228.  
  1229.    case 0xCC:                                     /* Indented paragraph */
  1230.               leave_environment(0);
  1231.               gobble(1); process1(Indent, igetc()); verify(0xCC);
  1232.               environment_status = eIndent; break;
  1233.                                           /* (really: Temporary margin) */
  1234.  
  1235.    case 0xCD:                          /* Indented paragraph (obsolete) */
  1236.               leave_environment(0);
  1237.               process1(Indent, igetc()); verify(0xCD);
  1238.               environment_status = eIndent; break;
  1239.                                           /* (really: Temporary margin) */
  1240.  
  1241.    case 0xCE: gobble(1); process1(TopMarg, igetc());  /* Set top margin */
  1242.               verify(0xCE); break;
  1243.  
  1244.    case 0xCF:                 /* Suppress headers/footers for this page */
  1245.               process1(Supp, (unsigned char)igetc());
  1246.               verify(0xCF); break;
  1247.  
  1248.    case 0xD0: gobble(2); /* old form length */       /* Set page length */
  1249.               process1(PageLength, igetc());          /* lines per page */
  1250.               gobble(1);                             /* new page length */
  1251.               verify(0xD0); break;
  1252.  
  1253.    case 0xD1:                                          /* header/footer */
  1254.               c = igetc();                                  /* def byte */
  1255.               gobble(1);                              /* old half-lines */
  1256.               if (c&2) { process0(Footer); FF_status = eFooter; }
  1257.                   else { process0(Header); FF_status = eHeader; }
  1258.               verify(0xFF); verify(0xFF);                  /* separator */
  1259.               gobble(2);                       /* left and right margin */
  1260.               break;                             /* continue processing */
  1261.  
  1262.    case 0xD2: gobble(5);                           /* obsolete footnote */
  1263.               unsupported(Obsolete);
  1264.               gobble_until(0xD2);
  1265.               break;
  1266.  
  1267.    case 0xD3: gobble(2);              /* obsolete `set footnote number' */
  1268.               unsupported(Obsolete);
  1269.               verify(0xD3);
  1270.               break;
  1271.  
  1272.    case 0xD4:                            /* Advance to half line number */
  1273.               c = igetc(); /* current line number */
  1274.               process2(AdvanceToHalfLine, c, igetc());/* desired line # */
  1275.               verify(0xD4); break;
  1276.  
  1277.    case 0xD5: gobble(1); process1(LPI, igetc());    /* Set LPI (6 or 8) */
  1278.               verify(0xD5); break;
  1279.  
  1280.    case 0xD6:                                      /* set extended tabs */
  1281.               /* next 4 bytes are <old start><old increment>
  1282.                                   <new start><new increment> */
  1283.               gobble(4);
  1284.               unsupported(SetTabs);
  1285.               verify(0xD6); break;
  1286.  
  1287.    case 0xD7: gobble(63);                        /* Define math columns */
  1288.               unsupported(DefineMathColumns);
  1289.               verify(0xD7); break;
  1290.  
  1291.    case 0xD8: gobble(1); process1(AlignChar, igetc());
  1292.               verify(0xD8); break;           /* Set alignment character */
  1293.  
  1294.    case 0xD9: gobble(2);                     /* obsolete margin release */
  1295.               unsupported(Obsolete);
  1296.               verify(0xD9);
  1297.               break;
  1298.  
  1299.    case 0xDA: gobble(1+1);                        /* Set underline mode */
  1300.               /* second byte is a bit field.
  1301.                *       1 = double-underline (default single),
  1302.                *       2 = underline spaces (default don't)
  1303.                */
  1304.               unsupported(SetUnderlineMode);
  1305.               verify(0xDA); break;
  1306.  
  1307.    case 0xDB:                                   /* Set sheet feeder bin */
  1308.               gobble(1); process1(SetBin, igetc());
  1309.               verify(0xDB); break;
  1310.  
  1311.    /* We ignore these codes, since they are followed by an 0x0C or an 0x8C */
  1312.    case 0xDC: gobble(7); verify(0xDC); break;      /* End-of-page codes */
  1313.  
  1314.    case 0xDD: gobble(22);                             /* define columns */
  1315.               unsupported(DefineColumn);
  1316.               verify(0xDD);
  1317.  
  1318.    case 0xDE: environment_status = 0;         /* End indented paragraph */
  1319.               gobble(2); process0(eIndent); verify(0xDE); break;
  1320.  
  1321.    case 0xDF:                                   /* invisible characters */
  1322.               gobble_until(0xDF);
  1323.               break;
  1324.  
  1325.    case 0xE0:                              /* Doubly-indented paragraph */
  1326.               leave_environment(0);
  1327.               gobble(1); process1(DIndent, igetc()); verify(0xE0);
  1328.               environment_status = eIndent; break;
  1329.  
  1330.    case 0xE1: process0((unsigned char)igetc()); verify(0xE1); break;
  1331.                                                        /* IBM character */
  1332.  
  1333.    case 0xE2: handle_note(); break;              /* footnote or endnote */
  1334.  
  1335.    case 0xE3: gobble(74+74);                     /* footnote attributes */
  1336.               unsupported(SetFootnoteAttributes);
  1337.               verify(0xE3);
  1338.               break;
  1339.  
  1340.    case 0xE4: gobble(2); /* old */               /* set footnote number */
  1341.               /* bit 7 of second byte doesn't count, and the value
  1342.                * is offset by one.
  1343.                */
  1344.               c = igetc() & 0x3f;
  1345.               c = (c << 7) + (igetc() & 0x7f);
  1346.               process1(SetFnNum, 1 + c);
  1347.               verify(0xE4);
  1348.               break;
  1349.  
  1350.    case 0xE5:                              /* paragraph numbering style */
  1351.               gobble(7+7+7+7);
  1352.               unsupported(SetParagraphNumberingStyle);
  1353.               verify(0xE5);
  1354.               break;
  1355.  
  1356.    case 0xE6:                                       /* paragraph number */
  1357.               gobble(2+7);
  1358.               unsupported(NumberedParagraph);
  1359.               verify(0xE6);
  1360.               break;
  1361.  
  1362.    case 0xE9:                                      /* begin marked text */
  1363.               gobble(6);
  1364.               unsupported(BeginMarkedText);
  1365.               verify(0xE9);
  1366.               break;
  1367.  
  1368.    case 0xEA:                                        /* end marked text */
  1369.               unsupported(EndMarkedText);
  1370.               gobble_until(0xEA);
  1371.               break;
  1372.  
  1373.    case 0xEB:                                     /* define marked text */
  1374.               gobble(30);
  1375.               unsupported(DefineMarkedText);
  1376.               verify(0xEB);
  1377.               break;
  1378.  
  1379.    case 0xEC:                                      /* define index mark */
  1380.               gobble(2);
  1381.               unsupported(DefineIndexMark);
  1382.               verify(0xEC);
  1383.               break;
  1384.  
  1385.    case 0xED:                                   /* Table of authorities */
  1386.               unsupported(DefineIndexMark);
  1387.               gobble_until(0xED);
  1388.               break;
  1389.    case 0xEE:                                   /* paragraph number def */
  1390.               gobble(42);
  1391.               unsupported(SetParagraphNumberingStyle);
  1392.               verify(0xEE);
  1393.               break;
  1394.  
  1395.    case 0xEF:                                       /* paragraph number */
  1396.               gobble(16);
  1397.               unsupported(NumberedParagraph);
  1398.               verify(0xEF);
  1399.               break;
  1400.  
  1401.    case 0xF1: gobble(32 + 20);                          /* Tab settings */
  1402.               handle_extended_tabs();
  1403.               verify(0xF1);
  1404.               break;
  1405.  
  1406.    case 0xF3:                                      /* column definition */
  1407.               gobble(98);
  1408.               unsupported(DefineColumn);
  1409.               verify(0xF3);
  1410.               break;
  1411.  
  1412.  
  1413.    case 0xB2:
  1414.    case 0xB3:
  1415.    case 0xB4:
  1416.    case 0xB5:
  1417.    case 0xB6:
  1418.    case 0xB7:
  1419.    case 0xB8:
  1420.    case 0xB9:
  1421.    case 0xBA:
  1422.  
  1423.    case 0xF0:
  1424.  
  1425.    case 0xF2:
  1426.    case 0xF4:
  1427.    case 0xF5:
  1428.    case 0xF6:
  1429.    case 0xF7:
  1430.    case 0xF8:
  1431.    case 0xF9:
  1432.    case 0xFA:
  1433.    case 0xFB:
  1434.    case 0xFC:
  1435.    case 0xFD:
  1436.    case 0xFE: unsupported(UnknownCode);  break;      /* undefined codes */
  1437.  
  1438.    case 0xFF: handle_FF(); break;
  1439.  
  1440.    default: process0(c); break;
  1441.   }
  1442.   return 1;
  1443. }
  1444.  
  1445. /* Now do the other Useful Function.
  1446.  */
  1447. void process_input(void)
  1448. {
  1449.   process0(BEGIN);
  1450.   while (process_token()) do_nothing;
  1451.   process0(END);
  1452. }
  1453.  
  1454.  
  1455. /************************************************************************/
  1456. /* The main program                                                     */
  1457. /************************************************************************/
  1458.  
  1459. /* First, a pretty little function which tries to open a file and
  1460.  * complains loudly if it cannot.
  1461.  */
  1462.  
  1463. FILE *efopen(const char *s, const char *m)
  1464. {
  1465.   FILE *fp = fopen(s, m);
  1466.  
  1467.   if (fp == NULL) {
  1468.     fprintf(stderr, "Error: Cannot open %s", s);
  1469.     if (errno > 0 && errno < sys_nerr)
  1470.         fprintf(stderr, " (%s)\n", s, sys_errlist[errno]);
  1471.     fprintf(stderr, "\n");
  1472.     exit(1);
  1473.   }
  1474.  
  1475.   return fp;
  1476. }
  1477.  
  1478. #include "dopen.c"            /* ickiness with file opening */
  1479.  
  1480. /* Our main program does very little, really.
  1481.  *
  1482.  * After checking the command line, it proceeds to open the descriptor
  1483.  * file in text mode, and the input file in binary mode.
  1484.  * It then calls our two Useful Functions in turn, closing each file
  1485.  * after it has served its purpose.
  1486.  */
  1487.  
  1488. int Cdecl main(int argc, char **argv)
  1489. {
  1490.   while (--argc && **++argv == '-') {
  1491.     while (*++*argv) switch (**argv) {
  1492.     case 's': silent = 1; break;
  1493.     case 'n': blipinterval = atoi(&argv[0][1]); goto finarg;
  1494.     default:  goto usage;
  1495.     }
  1496. finarg: ;
  1497.   }
  1498.   blipcount = blipinterval;
  1499.  
  1500.   if (argc != 2) {
  1501. usage:
  1502.     fprintf(stderr, "usage: wp2x descriptor input > output\n");
  1503.     exit(2);
  1504.   }
  1505.  
  1506.   dopen(argv[0]);
  1507.   input = efopen(argv[1], "rb");
  1508.  
  1509.   do_descriptor_file();
  1510.   fclose(descriptor);
  1511.  
  1512.   process_input();
  1513.   fclose(input);
  1514.   return 0;
  1515. }
  1516.